"""
清理前后端端口占用脚本（Python版本）
支持 Windows 和 Linux/Mac
"""
import subprocess
import sys
import platform
import time
import os

# 配置要清理的端口
PORTS = {
    "后端 (FastAPI)": 8000,
    "前端 (Vite)": 5173
}


def is_windows():
    """判断是否为Windows系统"""
    return platform.system() == "Windows"


def get_port_processes_windows(port):
    """Windows系统：获取占用指定端口的进程"""
    try:
        # 使用netstat查找进程
        result = subprocess.run(
            ['netstat', '-ano'],
            capture_output=True,
            text=True,
            encoding='gbk' if is_windows() else 'utf-8'  # Windows中文系统使用gbk编码
        )
        
        processes = []
        for line in result.stdout.split('\n'):
            # 更精确地匹配端口，避免误匹配包含该端口号的其他端口
            if f":{port} " in line and "LISTENING" in line:
                parts = line.split()
                if len(parts) >= 5:
                    pid = parts[-1]
                    if pid.isdigit():
                        processes.append(int(pid))
        
        return list(set(processes))  # 去重
    except Exception as e:
        print(f"获取端口进程失败: {e}")
        return []


def get_port_processes_unix(port):
    """Linux/Mac系统：获取占用指定端口的进程"""
    try:
        # 使用lsof查找进程（需要安装lsof）
        result = subprocess.run(
            ['lsof', '-ti', f':{port}'],
            capture_output=True,
            text=True
        )
        
        if result.returncode == 0 and result.stdout:
            pids = [int(pid) for pid in result.stdout.strip().split('\n') if pid]
            return pids
        return []
    except FileNotFoundError:
        print("提示：未安装lsof，尝试使用fuser...")
        try:
            result = subprocess.run(
                ['fuser', f'{port}/tcp'],
                capture_output=True,
                text=True
            )
            if result.returncode == 0 and result.stdout:
                pids = []
                for line in result.stdout.split():
                    if line.isdigit():
                        pids.append(int(line))
                return pids
        except FileNotFoundError:
            print("错误：未找到lsof或fuser命令")
            return []
    except Exception as e:
        print(f"获取端口进程失败: {e}")
        return []


def get_process_name_windows(pid):
    """Windows系统：获取进程名称"""
    try:
        result = subprocess.run(
            ['tasklist', '/FI', f'PID eq {pid}', '/FO', 'CSV'],
            capture_output=True,
            text=True,
            encoding='gbk' if is_windows() else 'utf-8'
        )
        for line in result.stdout.split('\n'):
            if ',' in line and 'INFO:' not in line:
                parts = line.split(',')
                if len(parts) >= 1:
                    return parts[0].strip('"')
    except:
        pass
    return None


def get_process_name_unix(pid):
    """Linux/Mac系统：获取进程名称"""
    try:
        result = subprocess.run(
            ['ps', '-p', str(pid), '-o', 'comm='],
            capture_output=True,
            text=True
        )
        if result.returncode == 0:
            return result.stdout.strip()
    except:
        pass
    return None


def kill_process_windows(pid):
    """Windows系统：终止进程"""
    try:
        # 首先尝试使用taskkill终止进程
        result = subprocess.run(['taskkill', '/F', '/PID', str(pid)], 
                               capture_output=True, text=True)
        if result.returncode == 0:
            return True
        else:
            print(f"终止进程 {pid} 失败: {result.stderr}")
            return False
    except subprocess.CalledProcessError as e:
        print(f"终止进程 {pid} 失败: {e}")
        return False
    except Exception as e:
        print(f"终止进程 {pid} 时发生异常: {e}")
        return False


def kill_process_unix(pid):
    """Linux/Mac系统：终止进程"""
    try:
        subprocess.run(['kill', '-9', str(pid)], 
                      capture_output=True, check=True)
        return True
    except subprocess.CalledProcessError as e:
        print(f"终止进程 {pid} 失败: {e}")
        return False


def wait_for_port_release(port, timeout=10):
    """等待端口释放"""
    start_time = time.time()
    while time.time() - start_time < timeout:
        if is_windows():
            pids = get_port_processes_windows(port)
        else:
            pids = get_port_processes_unix(port)
        
        if not pids:
            return True
        time.sleep(0.5)
    return False


def main():
    """主函数"""
    print("=" * 50)
    print("清理前后端端口占用脚本")
    print("=" * 50)
    print()
    print("目标端口：")
    for name, port in PORTS.items():
        print(f"  {name}: {port}")
    print()
    
    is_win = is_windows()
    total_killed = 0
    
    # 查找并显示占用端口的进程
    print("[1] 查找占用端口的进程...")
    print()
    
    port_pids = {}
    for name, port in PORTS.items():
        if is_win:
            pids = get_port_processes_windows(port)
        else:
            pids = get_port_processes_unix(port)
        
        port_pids[port] = pids
        
        if pids:
            print(f"  端口 {port} ({name}):")
            for pid in pids:
                if is_win:
                    proc_name = get_process_name_windows(pid)
                else:
                    proc_name = get_process_name_unix(pid)
                
                proc_info = f"PID: {pid}"
                if proc_name:
                    proc_info += f" | 进程: {proc_name}"
                print(f"    • {proc_info}")
        else:
            print(f"  端口 {port} ({name}): 未发现占用进程")
        print()
    
    # 统计需要清理的进程数
    need_kill = sum(len(pids) for pids in port_pids.values())
    
    if need_kill == 0:
        print("=" * 50)
        print("未发现需要清理的进程")
        print("=" * 50)
        return
    
    # 确认清理
    print("=" * 50)
    print(f"[2] 准备清理 {need_kill} 个进程...")
    print("=" * 50)
    print()
    
    try:
        confirm = input("是否继续清理？(y/N): ").strip().lower()
        if confirm not in ['y', 'yes', '是']:
            print("已取消清理操作")
            return
    except KeyboardInterrupt:
        print("\n已取消清理操作")
        return
    
    print()
    
    # 执行清理
    for name, port in PORTS.items():
        pids = port_pids.get(port, [])
        if not pids:
            continue
        
        print(f"[清理] 端口 {port} ({name}):")
        for pid in pids:
            if is_win:
                success = kill_process_windows(pid)
            else:
                success = kill_process_unix(pid)
            
            if success:
                print(f"  ✓ 成功终止进程 PID: {pid}")
                # 等待进程完全退出，端口释放
                if wait_for_port_release(port):
                    print(f"    端口 {port} 已成功释放")
                else:
                    print(f"    警告: 端口 {port} 可能仍在使用中")
                total_killed += 1
            else:
                print(f"  ✗ 终止失败 PID: {pid} (可能需要管理员权限)")
        print()
    
    # 完成提示
    print("=" * 50)
    print(f"清理完成！共清理 {total_killed} 个进程")
    print("=" * 50)
    
    if total_killed < need_kill:
        print()
        print("提示：部分进程清理失败，可能需要管理员权限")
        if is_win:
            print("Windows: 请以管理员身份运行此脚本")
        else:
            print("Linux/Mac: 请使用 sudo 运行此脚本")
    elif total_killed > 0:
        print()
        print("提示：建议重新启动相关服务前稍等片刻，确保系统完全释放端口资源")


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n\n操作已取消")
        sys.exit(0)
    except Exception as e:
        print(f"\n错误: {e}")
        sys.exit(1)